name: Sync Dependabot changeset
on:
  pull_request_target:
    paths:
      - '.github/workflows/sync_dependabot-changesets.yml'
      - '**/yarn.lock'

jobs:
  generate-changeset:
    runs-on: ubuntu-latest
    if: github.actor == 'dependabot[bot]' && github.repository == 'backstage/backstage'
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
        with:
          egress-policy: audit

      - name: Checkout
        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
        with:
          fetch-depth: 2
          ref: ${{ github.head_ref }}
          token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
      - name: Configure Git
        run: |
          git config --global user.email noreply@backstage.io
          git config --global user.name 'Github changeset workflow'
      - name: Generate changeset
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          script: |
            const { promises: fs } = require('fs');

            // Parses package.json files and returns the package names
            async function getPackagesNames(files) {
              const names = [];
              for (const file of files) {
                const data = JSON.parse(await fs.readFile(file, 'utf8'));
                if (!data.private) {
                  names.push(data.name);
                }
              }
              return names;
            }

            async function createChangeset(fileName, commitMessage, packages) {
              const pkgs = packages.map(pkg => `'${pkg}': patch`).join('\n');
              const message = commitMessage.replace(/([bB])ump ([\S]+)/, '$1ump `$2`').trim();
              const body = `---\n${pkgs}\n---\n\n${message}\n`;
              await fs.writeFile(fileName, body);
            }

            const branch = await exec.getExecOutput('git branch --show-current');
            if (!branch.stdout.startsWith('dependabot/')) {
              console.log('Not a dependabot branch, skipping');
              return;
            }

            const diffOutput = await exec.getExecOutput('git diff --name-only HEAD~1');
            const diffFiles = diffOutput.stdout.split('\n');

            if (diffFiles.find(f => f.startsWith('.changeset'))) {
              console.log('Changeset already exists, skipping');
              return;
            }

            const files = diffFiles
              .filter(file => file !== 'package.json') // skip root package.json
              .filter(file => file.includes('package.json'));

            const packageNames = await getPackagesNames(files);
            if (!packageNames.length) {
              console.log('No package.json changes to published packages, skipping');
              return;
            }

            const { stdout: shortHash } = await exec.getExecOutput('git rev-parse --short HEAD');
            const fileName = `.changeset/dependabot-${shortHash.trim()}.md`;
            const { stdout: commitMessage } = await exec.getExecOutput('git show --pretty=format:%s -s HEAD');
            await createChangeset(fileName, commitMessage, packageNames);
            await exec.exec('git', ['add', fileName]);
            await exec.exec('git commit -C HEAD --amend --no-edit');
            await exec.exec('git push --force');
